#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include "ShaderFactory.h"
#include "Shader.h"
#include "IRenderer.h"
#include "Game.h"

// StringVector_t - 
// UniformTypeElementPair_t - Type/ElementCount
// StringUniformTypeElementPair_t - UniformName/(Type/ElementCount)

typedef std::vector<std::string> StringVector_t;
typedef std::pair<Uniform::Type, unsigned int> UTEPair_t;
typedef std::pair<std::string, UTEPair_t> UUTEPair_t;
typedef std::vector<UUTEPair_t> UniformTypeElementsVector_t;

void CreateShader( std::string& fileName )
{
	std::ifstream file;
	file.open(fileName);

	if (!file.is_open())
		return;

	std::string shaderName;
	std::string line;
	StringVector_t vertexShaderNames;
	StringVector_t vertexShaderSources;
	StringVector_t fragmentShaderNames;
	StringVector_t fragmentShaderSources;
	StringVector_t* currentShaderVector = 0;
	Uniform::Vector_t uniformElements;
	Attribute::Vector_t attributeElements;
	
	Uniform::Type uniformType;
	Attribute::Type attribType;
	std::string typeString;
	std::string elementsString;
	unsigned int pos, pos2, elements;
	int currentVertexSource = -1;
	int currentFragmentSource = -1;
	int* currentSourceIndex = 0;
	
	file >> shaderName;

	do
	{
		getline(file, line);
		if (line == "VertexShader")
		{
			if (!currentSourceIndex) currentSourceIndex = &(currentVertexSource);
			getline(file, line);
			vertexShaderNames.push_back(line);
			currentShaderVector = &(vertexShaderSources);
			currentSourceIndex = &(currentVertexSource);
			++(*currentSourceIndex);
			(*currentShaderVector).push_back(std::string());
		} else if (line == "FragmentShader")
		{
			if (!currentSourceIndex) currentSourceIndex = &(currentFragmentSource);
			getline(file, line);
			fragmentShaderNames.push_back(line);
			currentShaderVector = &(fragmentShaderSources);
			currentSourceIndex = &(currentFragmentSource);
			++(*currentSourceIndex);
			(*currentShaderVector).push_back(std::string());
		} else if(line.length() > 0)
		{
			(*currentShaderVector)[*currentSourceIndex] += line + " ";
		}
	} while (!file.eof());
	
	file.close();

	for (StringVector_t::iterator it = vertexShaderSources.begin(); it != vertexShaderSources.end(); ++it)
	{
		line = *it;
		while (line.length() > 0)
		{
			pos = line.find("uniform");
			if (pos == std::string::npos)
				break;
			line = line.substr(pos+8);
		
			pos = line.find(" ");
			typeString = line.substr(0, pos);
			line = line.substr(pos+1);
			
			if (typeString == "float")
				uniformType = Uniform::VEC1;
			if (typeString == "vec2")
				uniformType = Uniform::VEC2;
			if (typeString == "vec3")
				uniformType = Uniform::VEC3;
			if (typeString == "vec4")
				uniformType = Uniform::VEC4;
			if (typeString == "mat2")
				uniformType = Uniform::MATRIX2;
			if (typeString == "mat3")
				uniformType = Uniform::MATRIX3;
			if (typeString == "mat4")
				uniformType = Uniform::MATRIX4;
			
			pos = line.find(";");
			pos2 = line.find("[");
			
			if (pos2 != std::string::npos && pos2 < pos)
			{
				elementsString = line.substr(pos2+1);
				elementsString.erase(elementsString.find("]"));
				elements = atoi(elementsString.c_str());
				pos = pos2;
			} else
			{
				elements = 1;
			}
			Uniform uniform;
			uniform.elements = elements;
			uniform.type = uniformType;
			uniform.name = line.substr(0, pos);
			uniformElements.push_back(uniform);
			line = line.substr(pos+1);
		}
	}
	for (StringVector_t::iterator it = fragmentShaderSources.begin(); it != fragmentShaderSources.end(); ++it)
	{
		line = *it;
		while (line.length() > 0)
		{
			pos = line.find("uniform");
			if (pos == std::string::npos)
				break;
			line = line.substr(pos+8);
		
			pos = line.find(" ");
			typeString = line.substr(0, pos);
			line = line.substr(pos+1);
			
			if (typeString == "float")
				uniformType = Uniform::VEC1;
			if (typeString == "vec2")
				uniformType = Uniform::VEC2;
			if (typeString == "vec3")
				uniformType = Uniform::VEC3;
			if (typeString == "vec4")
				uniformType = Uniform::VEC4;
			if (typeString == "mat2")
				uniformType = Uniform::MATRIX2;
			if (typeString == "mat3")
				uniformType = Uniform::MATRIX3;
			if (typeString == "mat4")
				uniformType = Uniform::MATRIX4;

			pos = line.find(";");
			pos2 = line.find("[");
			
			if (pos2 != std::string::npos && pos2 < pos)
			{
				elementsString = line.substr(pos2+1);
				elementsString.erase(elementsString.find("]"));
				elements = atoi(elementsString.c_str());
				pos = pos2;
			} else
			{
				elements = 1;
			}
			Uniform uniform;
			uniform.elements = elements;
			uniform.type = uniformType;
			uniform.name = line.substr(0, pos);
			uniformElements.push_back(uniform);
		}
	}
	for (StringVector_t::iterator it = vertexShaderSources.begin(); it != vertexShaderSources.end(); ++it)
	{
		line = *it;
		while (line.length() > 0)
		{
			pos = line.find("attribute");
			if (pos == std::string::npos)
				break;
			line = line.substr(pos+10);
		
			pos = line.find(" ");
			typeString = line.substr(0, pos);
			line = line.substr(pos+1);
			
			if (typeString == "float")
				attribType = Attribute::VEC1;
			if (typeString == "vec2")
				attribType = Attribute::VEC2;
			if (typeString == "vec3")
				attribType = Attribute::VEC3;
			if (typeString == "vec4")
				attribType = Attribute::VEC4;
			if (typeString == "mat2")
				attribType = Attribute::MATRIX2;
			if (typeString == "mat3")
				attribType = Attribute::MATRIX3;
			if (typeString == "mat4")
				attribType = Attribute::MATRIX4;
			
			pos = line.find(";");
			pos2 = line.find("[");
			
			if (pos2 != std::string::npos && pos2 < pos)
			{
				elementsString = line.substr(pos2+1);
				elementsString.erase(elementsString.find("]"));
				elements = atoi(elementsString.c_str());
				pos = pos2;
			} else
			{
				elements = 1;
			}
			Attribute attrib;
			attrib.elements = elements;
			attrib.type = attribType;
			attrib.name = line.substr(0, pos);
			attributeElements.push_back(attrib);
		}
	}
	for (StringVector_t::iterator it = fragmentShaderSources.begin(); it != fragmentShaderSources.end(); ++it)
	{
		line = *it;
		while (line.length() > 0)
		{
			pos = line.find("attribute");
			if (pos == std::string::npos)
				break;
			line = line.substr(pos+10);
		
			pos = line.find(" ");
			typeString = line.substr(0, pos);
			line = line.substr(pos+1);
			
			if (typeString == "float")
				attribType = Attribute::VEC1;
			if (typeString == "vec2")
				attribType = Attribute::VEC2;
			if (typeString == "vec3")
				attribType = Attribute::VEC3;
			if (typeString == "vec4")
				attribType = Attribute::VEC4;
			if (typeString == "mat2")
				attribType = Attribute::MATRIX2;
			if (typeString == "mat3")
				attribType = Attribute::MATRIX3;
			if (typeString == "mat4")
				attribType = Attribute::MATRIX4;
			
			pos = line.find(";");
			pos2 = line.find("[");
			
			if (pos2 != std::string::npos && pos2 < pos)
			{
				elementsString = line.substr(pos2+1);
				elementsString.erase(elementsString.find("]"));
				elements = atoi(elementsString.c_str());
				pos = pos2;
			} else
			{
				elements = 1;
			}
			Attribute attrib;
			attrib.elements = elements;
			attrib.type = attribType;
			attrib.name = line.substr(0, pos);
			attributeElements.push_back(attrib);
		}
	}
	
	std::vector<HashedString> shaderSources;

	for (int i = 0; i != vertexShaderSources.size(); ++i)
	{
		Renderer->CreateShaderSource( vertexShaderNames[i], vertexShaderSources[i], Shader::VertexShader );
		shaderSources.push_back(vertexShaderNames[i]);
	}
	for (int i = 0; i != fragmentShaderSources.size(); ++i)
	{
		Renderer->CreateShaderSource( fragmentShaderNames[i], fragmentShaderSources[i], Shader::FragmentShader );
		shaderSources.push_back(fragmentShaderNames[i]);
	}

	Renderer->CreateShader(HashedString(shaderName), shaderSources);

	for (Attribute::Vector_t::iterator it = attributeElements.begin(); it != attributeElements.end(); ++it)
		Renderer->LinkAttribute( shaderName, *it, it->type );

	for (Uniform::Vector_t::iterator it = uniformElements.begin(); it != uniformElements.end(); ++it)
		Renderer->LinkUniform( shaderName, *it, it->type );

	for (Uniform::Vector_t::iterator it = uniformElements.begin(); it != uniformElements.end(); ++it)
	{
		Pointer<IUniformInstruction> instruction;
		Game::getSingleton().CreateUniformInstruction(it->name, instruction);
		if (instruction.isValid())
		{
			instruction->count = it->elements;
			instruction->type = it->type;
			Renderer->getShader(HashedString(shaderName)).m_uniformInstructions.insert(IUniformInstruction::Pair_t(it->name, instruction));
		}
	}
}